/**
 * 事件发布与订阅模式
 * @constructor EventEmit
 */
import { v4 as uuidv4 } from 'uuid'

// 定义一个事件容器，用于装事件数组
const _caches = {}

export default class EventEmit {
  constructor() {
    Object.defineProperty(this, '_eventEmitId', { value: uuidv4() })
    _caches[this._eventEmitId] = {}
  }

  /**
   * 订阅的消息添进缓存列表
   * @param {String} key 订阅名称(事件名)
   * @param {Function} callback 回调函数(事件方法) 
   */
  listen (key, callback) {
    const id = this._eventEmitId
    // 判断evtCallbacks内有没有key事件容器，没有则创建一个新数组容器
    const cbs = getCache(id, key)
    if (!cbs) {
      setCache(id, key, [])
    }
    // 将事件存入
    getCache(id, key).push(callback)
  }
  /**
  * 发布消息，执行缓存列表
  * @param {String} key 订阅名称(事件名)
  * @param  {String} params 参数
  */
  trigger (key, ...params) {
    const cbs = getCache(this._eventEmitId, key)
    // 未注册该事件
    if (!cbs || !cbs.length) return false
    // 循环当前事件容器，传入当前params参数至cb函数并调用
    cbs.forEach((cb) => {
      return cb.apply(this, params)
    })
  }
  /**
   * 执行一次
   * @param {*} key 订阅名称(事件名)
   * @param {*} cb 回调函数(事件方法)
   */
  once (key, cb) {
    function on () {
      cb.apply(this, arguments)
      setTimeout(() => {
        this.remove(key, on)
      }, 0)
    }
    on.fn = cb
    this.listen(key, on)
  }
  /**
  * 解绑观察
  * @param {String} key 订阅名称(事件名)
  * @param {Function} fn 绑定的函数
  */
  remove (key, fn) {
    const cbs = getCache(this._eventEmitId, key)
    // 没有回调集合不执行
    if (!cbs) return false
    if (!fn) {
      // 未传递函数时，解绑所有函数
      cbs && cbs.length === 0
    } else {
      cbs.forEach((cb, i) => {
        if (cb === fn || cb.fn === fn) cbs.splice(i, 1)
      })
    }
  }
}
/**
 * 获取对象id下的缓存
 * @param {*} eventEmitId 对象id，唯一标识
 * @param {*} key 对象id下注册事件名称
 */
function getCache (eventEmitId, key) {
  return key ? _caches[eventEmitId][key] : _caches[eventEmitId]
}
/**
 * 给缓存池设置属性
 * @param {*} eventEmitId  对象id，唯一标识
 * @param {*} key  对象id下注册事件名称
 * @param {*} value 设置的值
 */
function setCache (eventEmitId, key, value) {
  _caches[eventEmitId][key] = value
}